package cn.bossfriday.im.common.poc.client;

import cn.bossfriday.common.exception.ServiceRuntimeException;
import cn.bossfriday.common.utils.GsonUtil;
import cn.bossfriday.common.utils.HttpUtils;
import cn.bossfriday.im.common.entity.result.Result;
import cn.bossfriday.im.common.enums.api.ApiRequestType;
import cn.bossfriday.im.common.helper.ImServerApiHelper;
import cn.bossfriday.im.common.message.api.enties.AccessServerAddress;
import cn.bossfriday.im.common.message.api.user.GetTokenRequest;
import cn.bossfriday.im.common.message.api.user.NavigateRequest;
import cn.bossfriday.im.common.message.api.user.NavigateResponse;
import cn.bossfriday.im.common.message.rpc.user.GetTokenOutput;
import cn.bossfriday.im.common.poc.model.PocUser;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * ImServerApiClient
 *
 * @author chenx
 */
public class ImServerApiClient {

    private ImServerApiClient() {
        // do noting
    }

    /**
     * getImToken
     *
     * @param apiServerRoot
     * @param appKey
     * @param appSecret
     * @param pocUser
     * @return
     */
    public static void getImToken(String apiServerRoot, String appKey, String appSecret, PocUser pocUser) throws IOException {
        CloseableHttpClient client = null;
        HttpPost post = null;
        try {
            String url = HttpUtils.combineUrl(apiServerRoot, ApiRequestType.USER_GET_TOKEN.getUrl());
            client = HttpUtils.getPoolingHttpClient(url);
            post = new HttpPost(url);
            applyApiAuthHeaders(post, appKey, appSecret);

            GetTokenRequest request = GetTokenRequest.builder()
                    .userId(pocUser.getUid())
                    .deviceId(pocUser.getDeviceId())
                    .userName(pocUser.getName())
                    .build();
            StringEntity requestEntity = new StringEntity(GsonUtil.toJson(request), StandardCharsets.UTF_8.toString());
            post.setEntity(requestEntity);

            String respStr = client.execute(post, new BasicResponseHandler());
            Result<GetTokenOutput> result = GsonUtil.fromJson(respStr, new TypeToken<Result<GetTokenOutput>>() {
            });

            if (!result.isOk()) {
                throw new ServiceRuntimeException("getImToken failed! code: " + result.getCode() + ", uid: " + pocUser.getUid());
            }

            if (Objects.isNull(result.getData()) || StringUtils.isEmpty(result.getData().getToken())) {
                throw new ServiceRuntimeException("getImToken failed!(token is null)" + " uid: " + pocUser.getUid());
            }

            pocUser.setImToken(result.getData().getToken());
        } finally {
            HttpUtils.releaseAndClose(client, post);
        }
    }

    /**
     * navigate
     *
     * @param apiServerRoot
     * @param appKey
     * @param appSecret
     * @param protocol
     * @param pocUser
     * @throws IOException
     */
    public static void navigate(String apiServerRoot, String appKey, String appSecret, int protocol, PocUser pocUser) throws IOException {
        CloseableHttpClient client = null;
        HttpPost post = null;
        try {
            String url = HttpUtils.combineUrl(apiServerRoot, ApiRequestType.USER_NAV.getUrl());
            client = HttpUtils.getPoolingHttpClient(url);
            post = new HttpPost(url);
            applyApiAuthHeaders(post, appKey, appSecret);

            NavigateRequest request = NavigateRequest.builder()
                    .token(pocUser.getImToken())
                    .build();
            StringEntity requestEntity = new StringEntity(GsonUtil.toJson(request), StandardCharsets.UTF_8.toString());
            post.setEntity(requestEntity);

            String respStr = client.execute(post, new BasicResponseHandler());
            Result<NavigateResponse> result = GsonUtil.fromJson(respStr, new TypeToken<Result<NavigateResponse>>() {
            });

            if (!result.isOk()) {
                throw new ServiceRuntimeException("navigate failed! code: " + result.getCode() + ", uid: " + pocUser.getUid());
            }

            NavigateResponse navResponse = result.getData();
            if (Objects.isNull(navResponse)) {
                throw new ServiceRuntimeException("navigate failed!(navResponse is null!)" + " uid: " + pocUser.getUid());
            }

            InetSocketAddress accessAddress = getPocInetSocketAddress(navResponse.getAccessAddressList(), protocol);
            pocUser.setAccessAddress(accessAddress);
        } finally {
            HttpUtils.releaseAndClose(client, post);
        }
    }

    /**
     * getPocInetSocketAddress
     * POC时使用第一个下发地址(客户端则应该选择最快地址);
     */
    private static InetSocketAddress getPocInetSocketAddress(List<AccessServerAddress> accessAddressList, int protocol) {
        if (CollectionUtils.isEmpty(accessAddressList)) {
            throw new ServiceRuntimeException("accessAddressList is empty!");
        }

        AccessServerAddress server = accessAddressList.stream()
                .filter(entry -> entry.getProtocol() == protocol)
                .findFirst()
                .orElseThrow(() -> new ServiceRuntimeException("protocol#" + protocol + " accessAddressList is empty!"));

        return server.toInetSocketAddress();
    }

    /**
     * applyApiAuthHeaders
     */
    private static void applyApiAuthHeaders(HttpRequestBase httpRequest, String appKey, String appSecret) {
        Map<String, String> headMap = ImServerApiHelper.getSignatureHeaderMap(appKey, appSecret);
        for (Map.Entry<String, String> entry : headMap.entrySet()) {
            httpRequest.addHeader(entry.getKey(), entry.getValue());
        }
    }
}
